/**
 * @license  Highcharts JS v6.1.2 (2018-08-31)
 *
 * Money Flow Index indicator for Highstock
 *
 * (c) 2010-2017 Grzegorz Blachliński
 *
 * License: www.highcharts.com/license
 */
'use strict';
(function (factory) {
	if (typeof module === 'object' && module.exports) {
		module.exports = factory;
	} else if (typeof define === 'function' && define.amd) {
		define(function () {
			return factory;
		});
	} else {
		factory(Highcharts);
	}
}(function (Highcharts) {
	(function (H) {




		var isArray = H.isArray,
		    reduce = H.reduce;

		    // Utils:
		function sumArray(array) {

		    return reduce(array, function (prev, cur) {
		        return prev + cur;
		    });
		}

		function toFixed(a, n) {
		    return parseFloat(a.toFixed(n));
		}

		function calculateTypicalPrice(point) {
		    return (point[1] + point[2] + point[3]) / 3;
		}

		function calculateRawMoneyFlow(typicalPrice, volume) {
		    return typicalPrice * volume;
		}
		/**
		 * The MFI series type.
		 *
		 * @constructor seriesTypes.mfi
		 * @augments seriesTypes.sma
		 */
		H.seriesType('mfi', 'sma',

		    /**
		     * Money Flow Index. This series requires `linkedTo` option to be set and
		     * should be loaded after the `stock/indicators/indicators.js` file.
		     *
		     * @extends plotOptions.sma
		     * @product highstock
		     * @sample {highstock} stock/indicators/mfi
		     *                     Money Flow Index Indicator
		     * @since 6.0.0
		     * @optionparent plotOptions.mfi
		     */

		    {
		        /**
		         * @excluding index
		         */
		        params: {
		            period: 14,
		            /**
		             * The id of volume series which is mandatory.
		             * For example using OHLC data, volumeSeriesID='volume' means
		             * the indicator will be calculated using OHLC and volume values.
		             *
		             * @type {String}
		             * @since 6.0.0
		             * @product highstock
		             */
		            volumeSeriesID: 'volume',
		            /**
		             * Number of maximum decimals that are used in MFI calculations.
		             *
		             * @type {Number}
		             * @since 6.0.0
		             * @product highstock
		             */
		            decimals: 4

		        }
		    }, {
		        nameBase: 'Money Flow Index',
		        getValues: function (series, params) {
		            var period = params.period,
		                xVal = series.xData,
		                yVal = series.yData,
		                yValLen = yVal ? yVal.length : 0,
		                decimals = params.decimals,
		                // MFI starts calculations from the second point
		                // Cause we need to calculate change between two points
		                range = 1,
		                volumeSeries = series.chart.get(params.volumeSeriesID),
		                yValVolume = volumeSeries && volumeSeries.yData,
		                MFI = [],
		                isUp = false,
		                xData = [],
		                yData = [],
		                positiveMoneyFlow = [],
		                negativeMoneyFlow = [],
		                newTypicalPrice,
		                oldTypicalPrice,
		                rawMoneyFlow,
		                negativeMoneyFlowSum,
		                positiveMoneyFlowSum,
		                moneyFlowRatio,
		                MFIPoint, i;

		            if (!volumeSeries) {
		                return H.error(
		                    'Series ' +
		                    params.volumeSeriesID +
		                    ' not found! Check `volumeSeriesID`.',
		                    true
		                );
		            }

		            // MFI requires high low and close values
		            if (
		                (xVal.length <= period) || !isArray(yVal[0]) ||
		                yVal[0].length !== 4 ||
		                !yValVolume
		            ) {
		                return false;
		            }
		            // Calculate first typical price
		            newTypicalPrice = calculateTypicalPrice(yVal[range]);
		            // Accumulate first N-points
		            while (range < period + 1) {
		                // Calculate if up or down
		                oldTypicalPrice = newTypicalPrice;
		                newTypicalPrice = calculateTypicalPrice(yVal[range]);
		                isUp = newTypicalPrice >= oldTypicalPrice ? true : false;
		                // Calculate raw money flow
		                rawMoneyFlow = calculateRawMoneyFlow(
		                    newTypicalPrice,
		                    yValVolume[range]
		                );
		                // Add to array
		                positiveMoneyFlow.push(isUp ? rawMoneyFlow : 0);
		                negativeMoneyFlow.push(isUp ? 0 : rawMoneyFlow);
		                range++;
		            }
		            for (i = range - 1; i < yValLen; i++) {
		                if (i > range - 1) {
		                    // Remove first point from array
		                    positiveMoneyFlow.shift();
		                    negativeMoneyFlow.shift();
		                    // Calculate if up or down
		                    oldTypicalPrice = newTypicalPrice;
		                    newTypicalPrice = calculateTypicalPrice(yVal[i]);
		                    isUp = newTypicalPrice > oldTypicalPrice ? true : false;
		                    // Calculate raw money flow
		                    rawMoneyFlow = calculateRawMoneyFlow(
		                        newTypicalPrice,
		                        yValVolume[i]
		                    );
		                    // Add to array
		                    positiveMoneyFlow.push(isUp ? rawMoneyFlow : 0);
		                    negativeMoneyFlow.push(isUp ? 0 : rawMoneyFlow);
		                }

		                // Calculate sum of negative and positive money flow:
		                negativeMoneyFlowSum = sumArray(negativeMoneyFlow);
		                positiveMoneyFlowSum = sumArray(positiveMoneyFlow);

		                moneyFlowRatio = positiveMoneyFlowSum / negativeMoneyFlowSum;
		                MFIPoint = toFixed(
		                    100 - (100 / (1 + moneyFlowRatio)),
		                    decimals
		                );
		                MFI.push([xVal[i], MFIPoint]);
		                xData.push(xVal[i]);
		                yData.push(MFIPoint);
		            }

		            return {
		                values: MFI,
		                xData: xData,
		                yData: yData
		            };
		        }
		    }
		);

		/**
		 * A `MFI` series. If the [type](#series.mfi.type) option is not
		 * specified, it is inherited from [chart.type](#chart.type).
		 *
		 * @type {Object}
		 * @since 6.0.0
		 * @extends series,plotOptions.mfi
		 * @excluding data,dataParser,dataURL
		 * @product highstock
		 * @apioption series.mfi
		 */

		/**
		 * An array of data points for the series. For the `mfi` series type,
		 * points are calculated dynamically.
		 *
		 * @type {Array<Object|Array>}
		 * @since 6.0.0
		 * @extends series.line.data
		 * @product highstock
		 * @apioption series.mfi.data
		 */

	}(Highcharts));
	return (function () {


	}());
}));
